/**
 *  测试用例管理路由
 *
 * Created by snowingsea on 2020/03/08.
 */
// const multer = require('multer');
const path = require('path');
const fs = require('fs-extra');
const compressing = require('compressing');
const { Types } = require('mongoose');
const middleware = require('./../middleware');
const files = require('./../../models/files');
const functions = require('./../../models/functions');
const fileElements = require('./../../models/file-elements');
const tests = require('./../../models/tests');
const testCaseGroups = require('../../models/test-case-groups');
const tasks = require('./../../models/tasks');
const testsExporter = require('./../../models/export/test-cases');
const PhoenixError = require('./../../models/phoenix-error');
const projectVersion = require('../../models/project-versions');
const ProjectVersion = require('../../models/mongo/project-version');
const cacheUtil = require('../../cacheUtil');
const utility = require('../../models/utility');
const uploadProgress = require('../middleware/upload-progress');
const breakPoints = require('./../../models/break-points');
const ProgressIndicator = require('../../models/progress-indicator');
const { eventHub } = require('../../models/event-hub');
const codeInstrumentations = require('../../models/code-instrumentation');
const { importTcfTests } = require('../../models/import/test-cases/tcf');
const { importDataTests } = require('../../models/import/test-cases/xuanyu');

/**
 * 获取测试用例
 * @param req
 * @param res
 * @param next
 */
function getTestCase(req, res, next) { // eslint-disable-line no-unused-vars
  res.json(tests.getJson(req.$testCase, req.$locale));
}

/**
 * 获取测试用例列表
 * @param req
 * @param res
 * @param next
 */
function getTestCases(req, res, next) {
  const resTests = {
    pagination: req.filter,
  };

  const options = tests.getTestOptions(req.query);
  options.functionId = req.$function._id; // eslint-disable-line no-underscore-dangle
  tests.filter(req.filter, options)
    .then((data) => {
      resTests.tests = data.map((testCase) => tests.getSummaryJson(testCase, req.$locale));
      return tests.getCount(options);
    })
    .then((count) => {
      resTests.pagination.total = count;
      res.json(resTests);
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 导出项目版本的测试用例集合
 * @param req
 * @param res
 * @param next
 */
function exportTests(req, res, next) {
  const { projectVersionId, format } = req.params;
  let fileIds = null;
  if (req.body.fileIds) {
    fileIds = req.body.fileIds.length === 0 ? null : req.body.fileIds;
  }
  const { $projectVersion, $project, $me } = req;
  const exporter = testsExporter.getTestCaseExporter(
    projectVersionId,
    format,
    req.$locale,
    $projectVersion,
    $project,
    $me,
    fileIds,
  );
  const progressIndicator = new ProgressIndicator();
  const chan = utility.getSessionId();
  progressIndicator.on('progress', (progress) => {
    if (chan) {
      eventHub.emit(chan, 'export', progress);
    }
  });
  exporter.exportTestCases(progressIndicator)
    .then(({ testsPath, cachePath }) => {
      res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
      res.download(testsPath, encodeURI(path.basename(testsPath)), () => fs.remove(cachePath));
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 导入项目版本的测试用例集合
 * @param req
 * @param res
 * @param next
 */
function importTests(req, res, next) {
  (async () => {
    if (!req.file) {
      throw new PhoenixError('invalid request', 'Missing parameters. "file" is required');
    }
    if (!req.body.fileType) {
      throw new PhoenixError('invalid request', 'Missing parameters. "import file type" is required');
    }
    if (req.body.sourcePath && !req.body.targetPath) {
      throw new PhoenixError('invalid request', 'Missing parameters. "targetPath" is required');
    }
    if (!req.body.sourcePath && req.body.targetPath) {
      throw new PhoenixError('invalid request', 'Missing parameters. "sourcePath" is required');
    }
    const { rootDirectory } = req.$projectVersion;
    let compiledCount = 0;
    if (rootDirectory && rootDirectory.compiledCount) {
      Object.keys(rootDirectory.compiledCount).forEach((key) => {
        if (rootDirectory.compiledCount[key] > 0) {
          compiledCount += rootDirectory.compiledCount[key];
        }
      });
    }
    if (compiledCount === 0) {
      throw new PhoenixError('no files to parsed');
    }
    const filePath = path.join(req.file.destination, req.file.filename);
    const fileOriginalName = req.file.originalname;
    const fileExtName = path.extname(fileOriginalName);
    switch (req.body.fileType) {
      case 'tcf': {
        await importTcfTests(filePath, req.$projectVersion, ['.zip'].includes(fileExtName));
        break;
      }
      case 'data': {
        await importDataTests(filePath, req.$projectVersion, ['.zip'].includes(fileExtName));
        break;
      }
      case 'xlsx/xlsm-614': {
        await tests.import614TestCases(filePath, req.$projectVersion, req.$locale, ['.zip'].includes(fileExtName), fileExtName);
        break;
      }
      case 'srt':
      case 'srt-func':
      default: {
        if (req.body.sourcePath) {
          req.body.sourcePath = (path.normalize(req.body.sourcePath)).replace(/\\/g, '/');
        }
        const progressIndicator = new ProgressIndicator();
        const chan = utility.getSessionId();
        progressIndicator.on('progress', (progress) => {
          if (chan) {
            eventHub.emit(chan, 'import', progress);
          }
        });
        await tests.importTests(
          filePath,
          req.$projectVersion,
          req.$project,
          req.$me,
          true,
          req.body.sourcePath,
          req.body.targetPath,
          false,
          false,
          progressIndicator
        );
      }
    }
    res.json();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

/**
 * 生成用例
 * @param req
 * @param res
 * @param next
 */
function generateTests(req, res, next) {
  (async () => {
    if (!req.$functions || req.$functions.length === 0) {
      next(new PhoenixError('invalid request', 'No function to generate'));
    }
    const { projectVersionId } = req.params;
    const fileMap = new Map();
    const fileIds = [];
    req.$files.forEach((file) => {
      const fileId = file._id;
      fileMap.set(fileId.toString(), file);
    });
    const funcs = [];
    req.$functions.forEach((func) => {
      if (!fileMap.has(func.fileId.toString())) {
        fileIds.push(func.fileId);
      }
      if (!fileElements.isTestableElement(func.kind)) {
        return;
      }
      funcs.push({
        _id: func._id,
        fileId: func.fileId,
        functionName: func.functionName,
        language: func.language,
        specialOriginalId: func.specialOriginalId,
        kind: func.kind,
        index: func.index,
        functionAttrs: func.functionAttrs,
        mangledId: func.mangledId,
      });
    });
    const resFiles = await files.getFiles(projectVersionId, { fileIds });
    resFiles.forEach((file) => {
      const fileId = file._id;
      fileMap.set(fileId.toString(), file);
    });
    const task = await tasks.addGenerateTask(funcs, fileMap, req.$project, req.$projectVersion, req.$me);
    if (!task) {
      next(new PhoenixError('invalid request', 'No function to generate'));
    } else {
      res.status(req.$hasCppFunction ? 206 : 202).json(tasks.getJson(task));
    }
  })().catch((err) => next(new PhoenixError(err)));
}
/**
 * 添加测试用例
 * @param req
 * @param res
 * @param next
 */
function addTestCase(req, res, next) {
  const projectId = req.$project._id; // eslint-disable-line no-underscore-dangle
  const projectVersionId = req.$projectVersion._id; // eslint-disable-line no-underscore-dangle
  const functionId = req.$function._id; // eslint-disable-line no-underscore-dangle
  const { fileId } = req.$function;
  const { versionType, languages, headers } = req.$projectVersion;
  const { groupId } = req.body;
  let functionVariables;
  functions.getFunctionVariables(functionId)
    .then((variables) => {
      // 处理集成测试variables
      if (versionType === projectVersion.versionType.integrationTest) {
        return functions.getRelationFuncsVariables(functionId);
      }
      return variables;
    }).then((variables) => {
      functionVariables = variables;
      if (versionType !== projectVersion.versionType.integrationTest) {
        return Promise.resolve();
      }
      const sourceFileExts = [];
      // 获取源码文件类型
      const suffixesFiles = {
        headers,
        ...languages,
      };
      Object.keys(suffixesFiles).forEach((key) => {
        if (suffixesFiles[key].suffixes) {
          sourceFileExts.push(...suffixesFiles[key].suffixes);
        }
      });
      return functions.IntegrationAddInvokeFuncFixedAddressBases({ versionId: projectVersionId, ...functionVariables }, sourceFileExts, versionType);
    })
    .then(async () => {
      const testCaseDatas = [].concat(req.$testCasesData).sort((a, b) => (a.number > b.number ? 1 : -1));
      const arr = [];
      // eslint-disable-next-line no-restricted-syntax
      for (const testCaseData of testCaseDatas) {
        tests.addDefaultValue(testCaseData, functionVariables);
        const copyId = req.body.copyId && testCaseData._id ? testCaseData._id : null;
        const {
          data, description, requirementInfo, userDefinedFields, typeName, testMethod,
        } = tests.getJson(testCaseData, req.$locale);
        // eslint-disable-next-line no-await-in-loop
        arr.push(await tests.addTest(
          projectId,
          projectVersionId,
          fileId,
          functionId,
          {
            data,
            description,
            requirementInfo,
            userDefinedFields,
            versionType: testCaseData.versionType,
            typeName,
            testMethod,
            copyId,
          },
          groupId,
        ));
      }
      return arr;
    })
    .then((testCases) => {
      let queue = Promise.resolve();
      if (groupId) {
        queue = functions.updateFunctionCoverage(functionId)
          .then(() => {
            if (req.$projectVersion.versionType !== projectVersion.versionType.integrationTest) {
              return Promise.resolve();
            }
            return functions.updateFunctionInvokeCoverage(functionId);
          });
      }
      return queue
        .then(() => testCases);
    })
    .then((testCases) => {
      const copyId = req.query.copyId || req.body.copyId;
      if (copyId) {
        // copy, return an array
        res.json(testCases.map((test) => tests.getJson(test, req.$locale)));
      } else {
        // create, return a single test case data
        const [testCase] = testCases;
        res.json(tests.getJson(testCase, req.$locale));
      }
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 批量更新（实际值赋给预期值）
 * @param {*} req
 * @param {*} res
 * @param {*} next
 * @returns
 */
function resultCopy(req, res, next) {
  const { testId } = req.params;
  tests.getTest(testId)
    .then((t) => tests.resultCopy(testId, t.data.output))
    .then(() => functions.updateFunctionCoverage(req.$testCase.functionId))
    .then(() => {
      if (req.$projectVersion.versionType !== projectVersion.versionType.integrationTest) {
        return Promise.resolve();
      }
      return functions.updateFunctionInvokeCoverage(req.$testCase.functionId);
    })
    .then(() => tests.getTest(testId))
    .then((testCase) => {
      res.json(tests.getJson(testCase, req.$locale));
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 编辑测试用例
 * @param req
 * @param res
 * @param next
 */
function editTestCase(req, res, next) {
  let { data } = req.body;
  const {
    key, value, description, requirementInfo, userDefinedFields, execCount, typeName, functionLevelApplied, testMethod,
  } = req.body;
  const { testId } = req.params;
  if (!data && !key && !description && !userDefinedFields && !execCount && !typeName) {
    next(new PhoenixError('invalid request', 'Missing parameters. "data", "key" or "description" or "execCount" or "typeName" is required'));
    return;
  }
  if (data) {
    try {
      data = JSON.parse(data);
    } catch (e) {
      next(new PhoenixError('invalid request', `Fail to parse data to JSON: ${e}`));
      return;
    }
  }
  const doc = {
    key,
    value,
    data,
    description,
    requirementInfo,
    userDefinedFields,
    execCount,
    typeName,
    testMethod,
  };
  if (req.$projectVersion.oldVersionId && (key || data)) {
    // 回归版本中修改用例数据后变更为新增用例
    doc.updateSourceInRegression = true;
  }
  let testIds = [testId];
  Promise.resolve()
    .then(async () => {
      if (functionLevelApplied && key === 'prelude') {
        testIds = (await tests.getTestsOfFunctions([req.$testCase.functionId], { idOnly: true })).map((test) => test._id);
      }
      if (Array.isArray(key)) {
        // 支持多个key
        return utility.arrayChunkOperation(testIds, 10, async (testId) => {
          for (let i = 0; i < key.length; i += 1) {
            // eslint-disable-next-line no-await-in-loop
            await tests.updateTest(testId, {
              ...doc,
              key: key[i],
            });
          }
        });
      }
      return utility.arrayChunkOperation(testIds, 10, (testId) => tests.updateTest(testId, doc));
    })
    .then(() => functions.updateFunctionCoverage(req.$testCase.functionId))
    .then(() => {
      if (req.$projectVersion.versionType !== projectVersion.versionType.integrationTest) {
        return Promise.resolve();
      }
      return functions.updateFunctionInvokeCoverage(req.$testCase.functionId);
    })
    .then(() => tests.getTest(testId))
    .then((testCase) => {
      res.json(tests.getJson(testCase, req.$locale));
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

function copyArrayElements(req, res, next) {
  (async () => {
    const { testId } = req.params;
    const { fieldPath, targetIndex } = req.body;
    const keyPaths = fieldPath.split('.');
    const atValue = keyPaths.pop();
    const idx = parseInt(keyPaths.pop(), 10);
    if (atValue !== '@value') {
      throw new PhoenixError('invalid request', 'malformed key path');
    }
    if (!Number.isInteger(idx)) {
      throw new PhoenixError('invalid request', 'must be an array index');
    }
    // 格式校验
    const reg = /^(\d+,)*\d+$/;
    const reg1 = /^\d+-\d+$/;
    // let keys = [];
    let indexes = [];
    if (reg.test(targetIndex)) {
      indexes = targetIndex.split(',').map((val) => parseInt(val, 10));
      // if (indexes.length > 100) {
      //   throw new PhoenixError('invalid request', 'index range must be less than 100');
      // }
      // 判断是否有重复
      if (new Set(indexes).size < indexes.length) {
        throw new PhoenixError('invalid request', 'index duplicated');
      }
    } else if (reg1.test(targetIndex)) {
      const [start, end] = targetIndex.split('-').map((val) => parseInt(val, 10));
      if (start >= end) {
        throw new PhoenixError('invalid request', 'range start should be less than end');
      }
      // if (end - start > 99) {
      //   throw new PhoenixError('invalid request', 'index range must be less than 100');
      // }
      for (let index = start; index <= end; index += 1) {
        indexes.push(index);
      }
    } else {
      throw new PhoenixError('invalid request', 'malformed value');
    }
    const testCase = await tests.getTest(testId);
    if (!testCase) {
      throw new PhoenixError('not found', 'testCase not found');
    }
    const val = utility.getValueByKeyPath(testCase.data, `${keyPaths.join('.')}.${idx}.@value`) || '';
    req.body = {
      key: indexes.map((idx) => `${keyPaths.join('.')}.${idx}.@value`),
      value: val,
    };
    next();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

/**
 * 删除测试用例
 * @param req
 * @param res
 * @param next
 */
function removeTestCase(req, res, next) {
  let upperAvailableTestGroupId;
  let groupIds = [];
  testCaseGroups.getTopGroupsByTestIds(
    [req.params.testId],
    req.$testCase.functionId
  )
    .then((resGroupIds) => {
      groupIds = resGroupIds;
      return tests.removeTest(req.params.testId, req.$testCase);
    })
    .then((testGroupId) => {
      upperAvailableTestGroupId = testGroupId;
      return Promise.all(
        groupIds.map((groupId) => testCaseGroups.removeTestResultByTopGroupId(
          groupId,
          req.$testCase.functionId
        ))
      );
    })
    .then(() => functions.updateFunctionCoverage(req.$testCase.functionId))
    .then(() => {
      if (req.$projectVersion.versionType !== projectVersion.versionType.integrationTest) {
        return Promise.resolve();
      }
      return functions.updateFunctionInvokeCoverage(req.$testCase.functionId);
    })
    .then(() => {
      res.status(200).json({ upperAvailableTestGroupId });
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 批量编辑测试用例 deprecated
 * @param req
 * @param res
 * @param next
 */
function editTestCases(req, res, next) {
  const { key, value } = req.query;
  const { functionId } = req.params;
  if (!key || !value) {
    next(new PhoenixError('invalid request', 'Missing parameters. "key" and "value" are required'));
    return;
  }

  tests.getTestsOfFunctions(functionId)
    .then((data) => {
      const promises = data.map((test) => {
        const testId = test._id; // eslint-disable-line no-underscore-dangle
        return tests.updateTest(testId, { key, value });
      });
      return Promise.all(promises);
    })
    .then(() => functions.updateFunctionCoverage(functionId))
    .then(() => {
      if (req.$projectVersion.versionType !== projectVersion.versionType.integrationTest) {
        return Promise.resolve();
      }
      return functions.updateFunctionInvokeCoverage(functionId);
    })
    .then(() => {
      res.json();
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 批量删除测试用例
 * @param req
 * @param res
 * @param next
 */
function removeTestCases(req, res, next) {
  (async () => {
    const { testIds } = req.body;
    const functionId = req.$function._id;
    let upperAvailableTestGroupId;
    const removeTestCaseIds = [];
    let promises = [];
    // 全部清除测试用例
    if (!testIds || testIds.length === 0) {
      const testCases = await tests.getTests({}, { functionId });
      promises = testCases.map((testCase) => {
        removeTestCaseIds.push(testCase._id);
        return () => tests.removeTest(testCase._id, testCase);
      });
    } else {
      promises = testIds.map((testId) => {
        removeTestCaseIds.push(testId);
        return () => tests.removeTest(testId);
      });
    }
    let resGroupIds = await utility.promiseChain(promises);
    // 重置测试用例和编组number;
    await tests.reshuffleTestNumber(functionId);
    await testCaseGroups.reshuffleGroupNumber(functionId);
    // 清除测试用例和对应编组执行结果
    resGroupIds = [].concat(resGroupIds);
    await Promise.all(resGroupIds.map((groupId) => {
      if (groupId !== null) {
        return testCaseGroups.removeTestResultByTopGroupId(
          groupId,
          functionId
        );
      }
      return Promise.resolve();
    }));

    // 有返回顶层的测试用例直接返回顶层
    if (resGroupIds.includes(null)) {
      upperAvailableTestGroupId = null;
    } else {
      const availableGroups = await testCaseGroups.getGroupById(resGroupIds);
      if (availableGroups.length > 0) {
        // 返回可用的上层编组id
        upperAvailableTestGroupId = availableGroups[0]._id;
      }
    }
    const topGroupIds = await testCaseGroups.getTopGroupsByTestIds(removeTestCaseIds, functionId);
    await Promise.all(
      topGroupIds.map((groupId) => testCaseGroups.removeGroupCoverage(groupId, functionId))
    );
    await functions.updateFunctionCoverage(functionId);
    if (req.$projectVersion.versionType === projectVersion.versionType.integrationTest) {
      await functions.updateFunctionInvokeCoverage(functionId);
    }
    res.status(200).json({ upperAvailableTestGroupId });
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
/**
 * 执行测试用例，导出目标机测试脚本的公用部分
 */
function runTestsCommon(req, res, next, startTaskFn) {
  if (!req.$functions || req.$functions.length === 0) {
    next(new PhoenixError('invalid request', 'No test case to run'));
    return;
  }

  const { projectVersionId } = req.params;
  const fileMap = new Map();
  const fileIds = [];
  const functionMap = new Map();
  const functionIds = [];
  req.$files.forEach((file) => {
    const fileId = file._id; // eslint-disable-line no-underscore-dangle
    fileMap.set(fileId.toString(), file);
  });
  req.$functions.forEach((func) => {
    const functionId = func._id; // eslint-disable-line no-underscore-dangle
    if (!fileMap.has(func.fileId.toString())) {
      fileIds.push(func.fileId);
    }
    functionMap.set(functionId.toString(), func);
    functionIds.push(functionId);
  });
  let testCases;
  tests.getTestsOfFunctions(functionIds)
    .then((res) => {
      testCases = res;
      if (testCases.length === 0) {
        return Promise.reject(new PhoenixError('invalid request', 'No test case to run'));
      }
      const promises = [
        files.getFiles(projectVersionId, { fileIds }),
        files.getTypes(fileIds),
        functions.getVariablesOfFunctions(functionIds),
        fileElements.getSpecializedParams(functionIds),
      ];
      return Promise.all(promises);
    })
    .then(([resFiles, fileTypeSystems, functionVariables, elementsSpecs]) => {
      const fileTypeSystemMap = new Map();
      fileTypeSystems.forEach((type) => {
        fileTypeSystemMap.set(type.fileId.toString(), type);
      });
      resFiles.forEach((file) => {
        const fileId = file._id.toString();
        file.fileTypeSystem = fileTypeSystemMap.has(fileId)
          ? fileTypeSystemMap.get(fileId) : {};
        fileMap.set(fileId, file);
      });
      functionVariables.forEach((variables) => {
        functionMap.get(variables.functionId.toString()).variables = variables;
      });
      elementsSpecs.forEach((elementSpecs, functionId) => {
        functionMap.get(functionId).specs = elementSpecs;
      });
      return startTaskFn(
        testCases,
        fileMap,
        functionMap,
        req.$project,
        req.$projectVersion,
        req.$me,
        req.query.coverageStatistics
      );
    })
    .then((task) => {
      if (!task) {
        next(new PhoenixError('invalid request', 'No test case to run'));
      } else {
        res.status(202).json(tasks.getJson(task));
      }
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 导出目标机测试脚本
 * @param req
 * @param res
 * @param next
 */
function exportTargetMachineTests(req, res, next) {
  runTestsCommon(req, res, next, async (...args) => {
    const { projectVersionId } = req.params;
    const { coverageStatistics } = req.query;
    const { fileIds = [], functionIds = [], description = '导出测试脚本' } = req.body;
    const ret = await codeInstrumentations.createCodeInstrumentation(
      projectVersionId,
      fileIds,
      functionIds,
      description,
      codeInstrumentations.type.targetMachine,
      coverageStatistics === '1'
    );
    return tasks.addExportTargetMachineTestTask(...args, ret._id);
  });
}

/**
 * 执行测试用例
 * @param req
 * @param res
 * @param next
 */
function runTests(req, res, next) {
  (async () => {
    if (req.$projectVersion.versionType === 'unitTest') {
      // 针对单元测试性能优化
      const functionIds = req.$functions.map((f) => f._id);
      const testCases = await tests.getTestsOfFunctions(functionIds);
      if (testCases.length === 0) {
        return Promise.reject(new PhoenixError('invalid request', 'No test case to run'));
      }
      const fileMap = new Map();
      const functionMap = req.$functions.reduce((map, item) => {
        map.set(item._id.toString(), item);
        fileMap.set(item.fileId.toString(), { _id: item.fileId });
        return map;
      }, new Map());
      const task = await tasks.addRunTask(
        testCases,
        fileMap,
        functionMap,
        req.$project,
        req.$projectVersion,
        req.$me,
        req.query.coverageStatistics,
      );
      if (!task) {
        throw new PhoenixError('invalid request', 'No test case to run');
      } else {
        return res.status(202).json(tasks.getJson(task));
      }
    } else {
      return runTestsCommon(req, res, next, (...args) => tasks.addRunTask(...args));
    }
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

/**
 * 执行部分测试用例
 * @param req
 * @param res
 * @param next
 */
function runPartialTests(req, res, next) {
  let testFunction;
  let fileId;
  const fileMap = new Map();
  const functionMap = new Map();
  // check function testable and get function & file info
  const { functionId } = req.params;
  if (!functionId) {
    throw new PhoenixError('invalid request', 'Missing parameters. "functionId" is required');
  }
  const { projectVersionId } = req.params;
  functions.getFunctions(projectVersionId, { functionIds: functionId })
    .then((resFunctions) => {
      req.$hasCppFunction = false;
      [testFunction] = resFunctions.filter((resFunction) => {
        if (!functions.isCFunction(resFunction)) {
          req.$hasCppFunction = true;
        }
        return fileElements.isTestableElement(resFunction.kind);
      });
      if (!testFunction) {
        throw new PhoenixError('invalid request', 'No test case to run');
      }
      return files.getCompilingFunction(testFunction);
    })
    .then((testableFunction) => {
      if (!testableFunction) {
        throw new PhoenixError('invalid request', 'No test case to run');
      }
      fileId = testableFunction.fileId;
      functionMap.set(testableFunction._id.toString(), testableFunction);
      const promises = [
        files.getFiles(projectVersionId, { fileIds: [fileId] }),
        files.getTypes([fileId]),
        functions.getVariablesOfFunctions([functionId]),
        fileElements.getSpecializedParams([Types.ObjectId(functionId)]),
      ];
      return Promise.all(promises);
    })
    .then(([resFiles, fileTypeSystems, functionVariables, elementsSpecs]) => {
      const fileTypeSystemMap = new Map();
      fileTypeSystems.forEach((type) => {
        fileTypeSystemMap.set(type.fileId.toString(), type);
      });
      resFiles.forEach((file) => {
        const fileId = file._id.toString();
        file.fileTypeSystem = fileTypeSystemMap.has(fileId)
          ? fileTypeSystemMap.get(fileId) : {};
        fileMap.set(fileId, file);
      });
      functionVariables.forEach((variables) => {
        functionMap.get(variables.functionId.toString()).variables = variables;
      });
      elementsSpecs.forEach((elementSpecs, functionId) => {
        functionMap.get(functionId).specs = elementSpecs;
      });
      if (req.$testCases.length === 0) {
        return Promise.reject(new PhoenixError('invalid request', 'No test case to run'));
      }
      return Promise.resolve();
    })
    // make sure req.query.groupIds ALWAYS exists
    .then(() => tasks.addRunTask(
      req.$testCases, // testCases include groupCases
      fileMap,
      functionMap,
      req.$project,
      req.$projectVersion,
      req.$me,
      req.query.coverageStatistics,
      {
        groupIds: req.query.groupIds,
        testCaseIds: req.$testCases.map(({ _id }) => _id),
        functionId,
      }
    ))
    .then((task) => {
      if (!task) {
        throw new PhoenixError('invalid request', 'No test case to run');
      } else {
        res.status(202).json(tasks.getJson(task));
      }
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

async function downloadTargetMachineTestsHelper(taskId) {
  const cacheDir = cacheUtil.getConfigPath('targetMachineDir');
  const dirs = await fs.readdir(cacheDir);
  const d = dirs.find((name) => name.endsWith(taskId));
  if (!d) {
    throw new Error(`test tarball (${taskId}) for target machine is not found`);
  }
  const p1 = path.join(cacheDir, d);
  const p2 = path.join(cacheDir, d.replace(/(.*)_[^_]*/, '$1'));
  await fs.rename(p1, p2);
  await compressing.zip.compressDir(p2, `${p2}.zip`);
  await fs.rmdir(p2, { recursive: true });
  return `${p2}.zip`;
}

/**
 * 下载目标机测试脚本
 * @param req
 * @param res
 * @param next
 */
function downloadTargetMachineTests(req, res, next) {
  const { taskId } = req.params;
  downloadTargetMachineTestsHelper(taskId)
    .then((zipPath) => {
      res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
      res.download(zipPath, encodeURI(path.basename(zipPath)), () => fs.remove(zipPath));
    })
    .catch((err) => next(new PhoenixError(err)));
}

/**
 * 一键测试
 * @param req
 * @param res
 * @param next
 */
function autoTest(req, res, next) {
  const {
    $files,
    $compilingFiles,
    $functions,
    $project,
    $projectVersion,
    $me,
  } = req;
  if ((!$compilingFiles || $compilingFiles.length === 0)
    && (!$functions || $functions.length === 0)) {
    next(new PhoenixError('invalid request', 'No file to analyse, no function to generate, no test case to run'));
    return;
  }

  tasks.addAutoTestTask(
    $files,
    $compilingFiles,
    $functions,
    $project,
    $projectVersion,
    $me,
    req.query.coverageStatistics,
    !!Number(req.query.deleteDuplicateCases)
  )
    .then((task) => {
      if (!task) {
        next(new PhoenixError('invalid request', 'No file to analyse, no function to generate, no test case to run'));
      } else {
        res.status(202).json(tasks.getJson(task));
      }
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

function regressionTest(req, res, next) {
  (async () => {
    const {
      $files,
      $compilingFiles,
      $functions,
      $project,
      $projectVersion,
      $me,
    } = req;
    if (utility.isEmpty(req.query.ignoreVerify) && $functions && $functions.length > 0) {
      const testCount = await functions.getTestCountByFunctions($functions.map((func) => func._id));
      if (testCount) {
        throw new PhoenixError('conflict', 'Functions already have testcases, please confirm');
      }
    }
    const task = await tasks.addAutoRegressionTest(
      $files,
      $compilingFiles,
      $functions,
      $project,
      $projectVersion,
      $me
    );
    if (!task) {
      next(new PhoenixError('invalid request', 'No file to analyse, no test case to migrate and run'));
    } else {
      res.status(202).json(tasks.getJson(task));
    }
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

/**
 * 项目总览-测试用例列表
 * @param req
 * @param res
 * @param next
 */
function allTestsOverview(req, res, next) {
  (async () => {
    const { projectVersionId: versionId } = req.params;
    const {
      sortBy, order, fileId, functionId, resultKind, kind, caseChange, tested,
    } = req.query;
    let { page, perPage } = req.query;
    page = parseInt(page, 10) || 1;
    perPage = parseInt(perPage, 10) || 100;
    const version = await ProjectVersion.findById(Types.ObjectId(versionId), [
      'projectId', 'versionName', 'coverages', 'testIdentifierRule', 'oldVersionId',
    ], { lean: true });
    version.isRegression = Boolean(version.oldVersionId);
    delete version.oldVersionId;
    const [list, total] = await tests.getOverview(versionId, {
      fileId,
      functionId,
      resultKind,
      kind,
      page,
      pageSize: perPage,
      sortBy,
      order,
      caseChange,
      tested: tested ? tested === 'true' || tested === '1' : undefined,
    });

    version.projectName = req.$project.projectName;
    const countOverview = await tests.testCountOverview(versionId, {
      fileId, functionId,
    });
    list.forEach((item) => {
      const testIdentifier = tests.getTestIdentifier({
        testIdentifierRule: version.testIdentifierRule,
        functionName: item.function.functionName,
        filepath: item.file.path,
        identifierNumber: item.identifierNumber,
      });
      item.identifier = testIdentifier;
    });
    res.json({
      version,
      countOverview,
      tests: list,
      pagination: {
        total,
        page,
        perPage,
        sortBy,
        order,
      },
    });
  })()
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 项目总览-测试用例列表下载xlsx
 * @param req
 * @param res
 * @param next
 */
function exportTestsOverview(req, res, next) {
  (async () => {
    const { projectVersionId: versionId } = req.params;
    let {
      tested,
    } = req.query;
    tested = tested ? tested === 'true' || tested === '1' : undefined;
    const versionInfo = await ProjectVersion.findById(Types.ObjectId(versionId), [
      'testIdentifierRule',
    ], { lean: true });
    const excelName = req.$project.projectName.concat(' ').concat(req.$projectVersion.versionName).concat('.用例列表.xlsx');
    const [xlsxFilePath, cachePath] = await tests.generateOverviewXlsx({
      versionId, testIdentifierRule: versionInfo.testIdentifierRule,
    }, excelName, req.$locale, tested);
    res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
    res.download(xlsxFilePath, encodeURI(path.basename(xlsxFilePath)), () => fs.remove(cachePath));
  })()
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 测试用例参数可选文件
 * @param {*} req
 * @param {*} res
 * @param {*} next
 */
function getTestCaseFiles(req, res, next) {
  (async () => {
    const { key, type, functionId } = req.query;
    const files = await tests.getTestCaseFiles(functionId, key, type);
    res.json({ files });
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
/**
 * 获取测试用例类型系统
 * @param  req
 * @param  res
 * @param  next
 */
function integrationGetFilesTypeSystemByTestId(req, res, next) {
  (async () => {
    const { testId } = req.params;
    const { types } = await tests.integrationGetFilesTypeSystemByTestId({ testCaseId: testId });
    res.json(types);
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

function getTestFieldConfig(req, res, next) {
  (async () => {
    const [result] = await tests.getTestFieldConfig(req.params.functionId, req.$projectVersion);
    res.json(result);
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
function updateTestFieldConfig(req, res, next) {
  (async () => {
    const { functionId } = req.params;
    const result = await tests.updateTestFieldConfig(functionId, req.body);
    res.json(result);
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
function addTestCaseGroup(req, res, next) {
  (async () => {
    const { parentId, description } = req.body;
    const group = await testCaseGroups.addGroup({
      projectId: req.$project._id,
      versionId: req.$projectVersion._id,
      versionType: req.$projectVersion.versionType,
      fileId: req.$file._id,
      functionId: req.$function._id,
      locale: req.$locale,
      parentId,
      description,
    });
    await functions.updateFunctionCoverage(req.$function._id);
    if (req.$projectVersion.versionType === projectVersion.versionType.integrationTest) {
      await functions.updateFunctionInvokeCoverage(req.$function._id);
    }
    res.json(group);
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
function editTestCaseGroup(req, res, next) {
  (async () => {
    await testCaseGroups.editTestCaseGroup(
      req.body,
      req.$group._id,
    );
    res.json();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

function moveTestCases(req, res, next) {
  (async () => {
    const { functionId } = req.params;
    const { testIds, targetGroupId } = req.body;
    await tests.moveTestCases(functionId, testIds, targetGroupId);
    res.json();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

function getTestCaseGroup(req, res, next) {
  (async () => {
    const {
      groupId, resultKind, kind, type,
    } = req.query;
    req.$function.testIdentifierRule = req.$projectVersion.testIdentifierRule;
    const tree = await testCaseGroups.getTree(groupId, req.$function, req.$locale, resultKind, kind);
    if (type === 'group') {
      utility.recursiveIter(tree, 'children', (obj) => {
        obj.children = obj.children.filter((item) => item.type === 'group');
      });
    }
    res.json(tree);
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

function addStubParamsOutput(req, res, next) {
  (async () => {
    if (!req.body.stubName) {
      throw new PhoenixError('not found', 'stubName not found');
    }
    const { stubName } = req.body;
    const { functionId } = req.$testCase;
    let funcVariable;
    if (req.$projectVersion.versionType === projectVersion.versionType.integrationTest) {
      funcVariable = await functions.getRelationFuncsVariables(functionId);
    } else {
      funcVariable = await functions.getFunctionVariables(functionId);
    }
    if (!funcVariable.stubs || !funcVariable.stubs[stubName]) {
      throw new PhoenixError('not found', `stubName: ${stubName} not found in function variable`);
    }
    await functions.addStubParamsOutput(functionId, stubName);
    await functions.removeResultOfExecution(functionId);
    res.status(204).json();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

function removeStubParamsOutput(req, res, next) {
  (async () => {
    if (!req.body.stubName) {
      throw new PhoenixError('not found', 'stubName not found');
    }
    await functions.removeStubParamsOutput(req.$testCase.functionId, req.body.stubName);
    await functions.removeResultOfExecution(req.$testCase.functionId);
    res.status(204).json();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

function addStubVariadicParams(req, res, next) {
  (async () => {
    if (!req.body.stubName) {
      throw new PhoenixError('not found', 'stubName not found');
    }
    if (!req.body.params) {
      throw new PhoenixError('invalid request', 'need params');
    }
    req.body.params.forEach((param) => {
      if (!param.type || !param.name) {
        throw new PhoenixError('invalid request', 'variadic param need type and name');
      }
    });
    const { stubName, fileId } = req.body;
    const { functionId } = req.$testCase;
    let funcVariable;
    if (req.$projectVersion.versionType === projectVersion.versionType.integrationTest) {
      funcVariable = await functions.getRelationFuncsVariables(functionId);
    } else {
      funcVariable = await functions.getFunctionVariables(functionId);
    }
    if (!funcVariable.stubs || (!funcVariable.stubs[stubName] && !funcVariable.stubs[`${stubName}@${fileId}`])) {
      throw new PhoenixError('not found', `stubName: ${stubName} not found in function variable`);
    }
    await tests.addStubVariadicParams(req.$testCase._id, req.body, funcVariable.stubs);
    const testInfo = await tests.getTest(req.$testCase._id);
    res.json(tests.getJson(testInfo, req.$locale));
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

function updateArrayIndexRange(req, res, next) {
  (async () => {
    const { testId } = req.params;
    const {
      key, value, times, stubNameFieldPath,
    } = req.body;
    if (!key || !value) {
      throw new PhoenixError('invalid request', 'need key and value');
    }
    if ((stubNameFieldPath && !times) || (!stubNameFieldPath && times)) {
      throw new PhoenixError('invalid request', 'need times and stubNameFieldPath');
    }
    await tests.updateArrayIndexRange(testId, key, value, stubNameFieldPath, times);
    res.json(req.body);
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
function updateBreakPointArrayIndexRange(req, res, next) {
  (async () => {
    const { breakPointId } = req.params;
    const { key, value } = req.body;
    if (!key || !value) {
      throw new PhoenixError('invalid request', 'need key and value');
    }
    await breakPoints.updateBreakPointArrayIndexRange(breakPointId, key, value);
    res.json();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
function addBreakPointPointTargets(req, res, next) {
  (async () => {
    const { breakPointId } = req.params;
    const {
      elementTypeName,
      len,
      typeName,
      pointerTargetName,
    } = req.body;
    if (!pointerTargetName) {
      throw new PhoenixError('invalid request', 'need pointerTargetName');
    }
    if (!elementTypeName && !typeName) {
      throw new PhoenixError('invalid request', 'need typeName');
    }
    if (elementTypeName && !len) {
      throw new PhoenixError('invalid request', 'array variable need len');
    }
    await breakPoints.addBreakPointPointTargets(
      breakPointId,
      pointerTargetName,
      typeName,
      elementTypeName,
      len,
    );
    res.json();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
function testCaseBreakPointList(req, res, next) {
  (async () => {
    const { testId } = req.params;
    const results = (await breakPoints.getBreakPointsByTestcase(testId, { breakPointId: req.query.breakPointId })).map((bp) => {
      const expandedVariables = { ...bp.expandedVariables };
      delete bp.expandedVariables;
      return { ...expandedVariables, ...bp };
    });
    res.json(results);
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
function updateBreakPointVaraibleValue(req, res, next) {
  (async () => {
    const { breakPointId } = req.params;
    const { key, value } = req.body;
    if (!key) {
      throw new PhoenixError('invalid request', 'need key');
    }
    if (value === undefined) {
      throw new PhoenixError('invalid request', 'need value');
    }
    await breakPoints.updateBreakPointVaraibleValue(breakPointId, req.$breakPoint, key, value);
    res.json();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

function addBreakPoint(req, res, next) {
  (async () => {
    if (!req.body.fileId) {
      throw new PhoenixError('invalid request', 'need fileId');
    }
    if (!req.body.functionId) {
      throw new PhoenixError('invalid request', 'need functionId');
    }
    const breakPoint = await breakPoints.upsertBreakPoint(
      null,
      {
        ...req.body,
        versionId: req.$testCase.versionId,
        testcaseId: req.$testCase._id,
        entryFunctionId: req.$testCase.functionId,
      },
      req.$projectVersion.versionType,
    );
    res.json(breakPoints.getJson(breakPoint));
  })().catch((err) => {
    if (err.code === 11000) {
      err = new PhoenixError('conflict', 'breakpoint already exist');
    }
    next(new PhoenixError(err));
  });
}

function editBreakPoint(req, res, next) {
  (async () => {
    if (!req.body.fileId) {
      throw new PhoenixError('invalid request', 'need fileId');
    }
    if (!req.body.functionId) {
      throw new PhoenixError('invalid request', 'need functionId');
    }
    req.body.entryFunctionId = req.$testCase.functionId;
    const breakPoint = await breakPoints.upsertBreakPoint(
      req.params.breakPointId,
      req.body,
      req.$projectVersion.versionType
    );
    res.json(breakPoints.getJson(breakPoint));
  })().catch((err) => {
    if (err.code === 11000) {
      err = new PhoenixError('conflict', 'breakpoint already exist');
    }
    next(new PhoenixError(err));
  });
}

function getBreakPoint(req, res, next) {
  (async () => {
    res.json(breakPoints.getJson(req.$breakPoint));
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

function deleteBreakPoint(req, res, next) {
  (async () => {
    if (!req.params.breakPointId) {
      throw new PhoenixError('invalid request', 'need breakPointId');
    }
    await breakPoints.deleteBreakPoint({ _id: req.params.breakPointId });
    res.status(204).json();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

function getObservableVariables(req, res, next) {
  (async () => {
    const { fileId, line } = req.query;
    if (!fileId) {
      throw new PhoenixError('invalid request', 'need fileId');
    }
    if (!line) {
      throw new PhoenixError('invalid request', 'need line');
    }
    const observableVariables = await breakPoints.getObservableVariables(req.$testCase.functionId, fileId, line, req.$projectVersion.versionType);
    res.json(observableVariables);
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

function peekNextTypeNumber(req, res, next) {
  (async () => {
    const { typeName, functionId } = req.query;
    if (!typeName || !functionId) {
      throw new PhoenixError('invalid request', 'need type and functionId');
    }
    const typeNumber = await tests.peekNextTypeNumberString(functionId, typeName);
    res.json({
      typeNumber,
    });
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
function bulkDeleteTestcases(req, res, next) {
  (async () => {
    const { functionIds } = req.body;
    if (!functionIds) {
      throw new PhoenixError('invalid request', 'need functionIds');
    }
    await utility.arrayChunkOperation(functionIds, 10, async (functionId) => {
      let promises = [];
      const func = await functions.getFunction(functionId);
      if (!func) {
        return;
      }
      const version = await ProjectVersion.findById(func.versionId);
      if (!version) {
        return;
      }
      const testCases = await tests.getTests({}, { functionId });
      promises = testCases.map((testCase) => () => tests.removeTest(testCase._id, testCase));
      let resGroupIds = await utility.promiseChain(promises);
      await tests.reshuffleTestNumber(functionId);
      await testCaseGroups.reshuffleGroupNumber(functionId);
      resGroupIds = [].concat(resGroupIds);
      await Promise.all(resGroupIds.map((groupId) => {
        if (groupId !== null) {
          return testCaseGroups.removeTestResultByTopGroupId(
            groupId,
            functionId
          );
        }
        return Promise.resolve();
      }));
      await functions.updateFunctionCoverage(functionId);
      if (version.versionType === projectVersion.versionType.integrationTest) {
        await functions.updateFunctionInvokeCoverage(functionId);
      }
    });
    res.json();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
function resetTestNumber(req, res, next) {
  (async () => {
    await tests.resetTestNumber(req.params.functionId, req.$locale);
    res.status(204).json();
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}
function getTestcaseCombined(req, res, next) { // eslint-disable-line no-unused-vars
  (async () => {
    const { testId } = req.params;
    if (!testId) {
      next(new PhoenixError('invalid request', 'Missing parameters. "testId" is required'));
      return;
    }
    const testcase = await tests.getTest(testId);
    if (!testcase) {
      next(new PhoenixError('not found', 'Not found, testCase not found'));
      return;
    }
    const { functionId, versionType } = testcase;
    let funcVariables = {};
    // 处理集成测试variables
    if (versionType === projectVersion.versionType.integrationTest) {
      funcVariables = await functions.getRelationFuncsVariablesV2(functionId);
    } else {
      funcVariables = await functions.getFunctionVariables(functionId);
      funcVariables = functions.getFunctionVariableJson(funcVariables);
      funcVariables.mangledId = new Set();
    }
    const usedTypes = await functions.getUsedTypes(funcVariables, testcase);
    const func = await functions.getRawFunction(functionId);
    // 参与集成的函数 fileId_mangledId
    funcVariables.mangledId.add(`${func.fileId}_${func.mangledId}`);
    funcVariables.mangledId = [...funcVariables.mangledId];
    res.json({
      variables: funcVariables,
      testcase: tests.getJson(testcase, req.$locale),
      types: versionType === projectVersion.versionType.integrationTest
        ? usedTypes
        : {
          fileId: testcase.fileId,
          types: Object.values(usedTypes)[0] || {},
        },
    });
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

/**
 * Export model definition object.
 */
module.exports = (app, prefix) => {
  const allTestsURL = `${prefix}/project-versions/:projectVersionId/tests.:format`;
  const allTestsExportURL = `${prefix}/project-versions/:projectVersionId/export/tests.:format`;
  const resultsURL = `${prefix}/project-versions/:projectVersionId/results.json`;
  const partialResultsURL = `${prefix}/project-versions/:projectVersionId/functions/:functionId/partial-results.json`;
  const targetMachineTestsExportUrl = `${prefix}/project-versions/:projectVersionId/export/target-machine-tests.json`;
  const targetMachineTestsDownloadUrl = `${prefix}/project-versions/:projectVersionId/target-machines/download/:taskId`;
  const testURL = `${prefix}/tests/:testId.json`;
  const testResultCopyURL = `${prefix}/:testId/output/result-copy.json`;
  const testTypeSystemURL = `${prefix}/tests/:testId/types.json`;
  const testsURL = `${prefix}/functions/:functionId/tests.json`;
  const testsFieldConfigURL = `${prefix}/functions/:functionId/test-field-config.json`;
  const testFilesURL = `${prefix}/test/files.json`;
  const testCaseGroupURL = `${prefix}/functions/:functionId/testcase-groups.json`;
  const bulkDeleteTestcaseURL = `${prefix}/functions/delete/testcases.json`;
  const editTestCaseGroupURL = `${prefix}/functions/testcase-groups/:groupId.json`;
  const allTestsOverviewUrl = `${prefix}/project-versions/:projectVersionId/tests/overview.json`;
  const downloadTestsOverviewUrl = `${prefix}/project-versions/:projectVersionId/tests/overview.xlsx`;
  const upload = uploadProgress;
  const regressionResultsURL = `${prefix}/project-versions/:projectVersionId/regression/results.json`;
  const stubParamsOutputURL = `${prefix}/tests/:testId/output/stub-params.json`;
  const stubVariadicParamsURL = `${prefix}/tests/:testId/stubs/variadic-params.json`;

  const arrayIndexRangeURL = `${prefix}/tests/:testId/array-index-range.json`;
  const breakPointsURL = `${prefix}/tests/:testId/break-points.json`;
  const breakPointURL = `${prefix}/tests/:testId/break-points/:breakPointId.json`;
  const observableVariableURL = `${prefix}/tests/:testId/break-points/observable-variables.json`;
  const breakPointArrayIndexRangeURL = `${prefix}/tests/break-points/:breakPointId/array-index-range.json`;
  const breakPointPointTargetsURL = `${prefix}/tests/break-points/:breakPointId/point-targets.json`;
  const testCaseBreakPointListURL = `${prefix}/tests/:testId/break-point-list.json`;
  const breakPointVariableURL = `${prefix}/tests/break-points/variable/:breakPointId.json`;
  const testsNumberURL = `${prefix}/functions/:functionId/testcase-number`;

  const nextTypeNumberURL = `${prefix}/tests/next-type-number.json`;

  const copyArrarElementUrl = `${prefix}/tests/:testId/copy-array-elements.json`;
  const moveTestCaseUrl = `${prefix}/functions/:functionId/move-tests.json`;
  const testcaseCombinedURL = `${prefix}/tests/:testId/combined.json`;

  // TODO 生成 执行 导入 只能在解析成功下完成
  // 获取测试用例所有类型系统
  app.get(testTypeSystemURL, middleware.user.requireTokenUnexpired);
  app.get(testTypeSystemURL, middleware.user.requireAvailable);
  app.get(testTypeSystemURL, middleware.user.requireTester);
  app.get(testTypeSystemURL, middleware.testCase.requireTestCaseExist);
  app.get(testTypeSystemURL, middleware.project.requireProjectAccess);
  app.get(testTypeSystemURL, integrationGetFilesTypeSystemByTestId);

  // 获取某个用例分组的下一个编号
  app.get(nextTypeNumberURL, middleware.user.requireTokenUnexpired);
  app.get(nextTypeNumberURL, middleware.user.requireAvailable);
  app.get(nextTypeNumberURL, middleware.user.requireTester);
  app.get(nextTypeNumberURL, peekNextTypeNumber);

  // 获取测试用例
  app.get(testURL, middleware.user.requireTokenUnexpired);
  app.get(testURL, middleware.user.requireAvailable);
  app.get(testURL, middleware.user.requireTester);
  app.get(testURL, middleware.testCase.requireTestCaseExist);
  app.get(testURL, middleware.project.requireProjectAccess);
  app.get(testURL, getTestCase);

  // 获取测试用例参数可选列表
  app.get(testFilesURL, middleware.user.requireTokenUnexpired);
  app.get(testFilesURL, middleware.user.requireAvailable);
  app.get(testFilesURL, middleware.user.requireTester);
  app.get(testFilesURL, getTestCaseFiles);

  // 获取测试用例列表
  app.get(testsURL, middleware.user.requireTokenUnexpired);
  app.get(testsURL, middleware.user.requireAvailable);
  app.get(testsURL, middleware.user.requireTester);
  app.get(testsURL, middleware.function.requireFunctionExist);
  app.get(testsURL, middleware.project.requireProjectAccess);
  app.get(testsURL, middleware.splitFilter);
  app.get(testsURL, getTestCases);

  // 导出项目版本测试用例
  app.post(allTestsExportURL, middleware.user.requireTokenUnexpired);
  app.post(allTestsExportURL, middleware.user.requireAvailable);
  app.post(allTestsExportURL, middleware.user.requireTester);
  app.post(allTestsExportURL, middleware.projectVersion.requireProjectVersionExist);
  app.post(allTestsExportURL, middleware.user.getLockByVersion());
  app.post(allTestsExportURL, middleware.project.requireProjectAccess);
  app.post(allTestsExportURL, middleware.progressNotify([{ name: 'export', scale: 1 }]));
  app.post(allTestsExportURL, exportTests);

  // 生成用例
  app.post(allTestsURL, middleware.user.requireTokenUnexpired);
  app.post(allTestsURL, middleware.user.requireAvailable);
  app.post(allTestsURL, middleware.user.requireTester);
  app.post(allTestsURL, middleware.projectVersion.requireProjectVersionExist);
  app.post(allTestsURL, middleware.user.getLockByVersion());
  app.post(allTestsURL, middleware.project.requireProjectAccess);
  app.post(allTestsURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.post(allTestsURL, middleware.project.requireProjectAvailable);
  app.post(allTestsURL, middleware.function.validation);
  app.post(allTestsURL, middleware.file.parseFileListQuery);
  app.post(allTestsURL, middleware.function.parseFunctionListQuery);
  app.post(allTestsURL, middleware.file.getFiles);
  app.post(allTestsURL, middleware.function.getFunctions);
  app.post(allTestsURL, middleware.file.filterFilesAvailable);
  app.post(allTestsURL, middleware.function.filterUntestableFunctions);
  app.post(allTestsURL, generateTests);

  // 导入项目版本测试用例
  app.put(allTestsURL, middleware.user.requireTokenUnexpired);
  app.put(allTestsURL, middleware.user.requireAvailable);
  app.put(allTestsURL, middleware.user.requireTester);
  app.put(allTestsURL, middleware.projectVersion.requireProjectVersionExist);
  app.put(allTestsURL, middleware.user.getLockByVersion());
  app.put(allTestsURL, middleware.project.requireProjectAccess);
  app.put(allTestsURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.put(allTestsURL, middleware.project.requireProjectAvailable);
  app.put(allTestsURL, upload.single('file'));
  app.put(allTestsURL, middleware.progressNotify([{ name: 'uncompress', scale: 0.1 }, { name: 'import', scale: 0.9 }]));
  app.put(allTestsURL, importTests);

  // 添加测试用例
  app.post(testsURL, middleware.user.requireTokenUnexpired);
  app.post(testsURL, middleware.user.requireAvailable);
  app.post(testsURL, middleware.user.requireTester);
  app.post(testsURL, middleware.function.requireFunctionExist);
  app.post(testsURL, middleware.user.getLockByVersion());
  app.post(testsURL, middleware.project.requireProjectAccess);
  app.post(testsURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.post(testsURL, middleware.project.requireProjectAvailable);
  app.post(testsURL, middleware.testCase.parseTestCaseData);
  app.post(testsURL, middleware.file.requireFileAvailable);
  app.post(testsURL, addTestCase);

  // 编辑测试用例
  app.put(testURL, middleware.user.requireTokenUnexpired);
  app.put(testURL, middleware.user.requireAvailable);
  app.put(testURL, middleware.user.requireTester);
  app.put(testURL, middleware.testCase.requireTestCaseExist);
  app.put(testURL, middleware.user.getLockByVersion());
  app.put(testURL, middleware.project.requireProjectAccess);
  app.put(testURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.put(testURL, middleware.project.requireProjectAvailable);
  app.put(testURL, middleware.file.requireFileAvailable);
  app.put(testURL, editTestCase);

  // 复制数组下标值
  app.put(copyArrarElementUrl, middleware.user.requireTokenUnexpired);
  app.put(copyArrarElementUrl, middleware.user.requireAvailable);
  app.put(copyArrarElementUrl, middleware.user.requireTester);
  app.put(copyArrarElementUrl, middleware.testCase.requireTestCaseExist);
  app.put(copyArrarElementUrl, middleware.user.getLockByVersion());
  app.put(copyArrarElementUrl, middleware.project.requireProjectAccess);
  app.put(copyArrarElementUrl, middleware.projectVersion.requireProjectVersionAvailable);
  app.put(copyArrarElementUrl, middleware.project.requireProjectAvailable);
  app.put(copyArrarElementUrl, middleware.file.requireFileAvailable);
  app.put(copyArrarElementUrl, copyArrayElements, editTestCase);

  // 批量移动测试用例
  app.put(moveTestCaseUrl, middleware.user.requireTokenUnexpired);
  app.put(moveTestCaseUrl, middleware.user.requireAvailable);
  app.put(moveTestCaseUrl, middleware.user.requireTester);
  app.put(moveTestCaseUrl, middleware.function.requireFunctionExist);
  // app.put(moveTestCaseUrl, middleware.user.getLockByVersion());
  app.put(moveTestCaseUrl, middleware.project.requireProjectAccess);
  // app.put(moveTestCaseUrl, middleware.projectVersion.requireProjectVersionAvailable);
  // app.put(moveTestCaseUrl, middleware.project.requireProjectAvailable);
  app.put(moveTestCaseUrl, moveTestCases);

  // 批量更新（实际值赋给预期值）
  app.put(testResultCopyURL, middleware.user.requireTokenUnexpired);
  app.put(testResultCopyURL, middleware.user.requireAvailable);
  app.put(testResultCopyURL, middleware.user.requireTester);
  app.put(testResultCopyURL, middleware.testCase.requireTestCaseExist);
  app.put(testResultCopyURL, middleware.user.getLockByVersion());
  app.put(testResultCopyURL, middleware.project.requireProjectAccess);
  app.put(testResultCopyURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.put(testResultCopyURL, middleware.project.requireProjectAvailable);
  app.put(testResultCopyURL, middleware.file.requireFileAvailable);
  app.put(testResultCopyURL, resultCopy);

  // 删除测试用例
  app.delete(testURL, middleware.user.requireTokenUnexpired);
  app.delete(testURL, middleware.user.requireAvailable);
  app.delete(testURL, middleware.user.requireTester);
  app.delete(testURL, middleware.testCase.requireTestCaseExist);
  app.delete(testURL, middleware.user.getLockByVersion());
  app.delete(testURL, middleware.project.requireProjectAccess);
  app.delete(testURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.delete(testURL, middleware.project.requireProjectAvailable);
  app.delete(testURL, middleware.file.requireFileAvailable);
  app.delete(testURL, removeTestCase);

  // 批量编辑测试用例
  app.put(testsURL, middleware.user.requireTokenUnexpired);
  app.put(testsURL, middleware.user.requireAvailable);
  app.put(testsURL, middleware.user.requireTester);
  app.put(testsURL, middleware.function.requireFunctionExist);
  app.put(testsURL, middleware.user.getLockByVersion());
  app.put(testsURL, middleware.project.requireProjectAccess);
  app.put(testsURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.put(testsURL, middleware.project.requireProjectAvailable);
  app.put(testsURL, editTestCases);

  // 批量删除测试用例
  app.delete(testsURL, middleware.user.requireTokenUnexpired);
  app.delete(testsURL, middleware.user.requireAvailable);
  app.delete(testsURL, middleware.user.requireTester);
  app.delete(testsURL, middleware.function.requireFunctionExist);
  app.delete(testsURL, middleware.user.getLockByVersion());
  app.delete(testsURL, middleware.project.requireProjectAccess);
  app.delete(testsURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.delete(testsURL, middleware.project.requireProjectAvailable);
  app.delete(testsURL, removeTestCases);

  // 执行测试用例
  app.put(resultsURL, middleware.license.detectSystemTimeTampering);
  app.put(resultsURL, middleware.user.requireTokenUnexpired);
  app.put(resultsURL, middleware.user.requireAvailable);
  app.put(resultsURL, middleware.user.requireTester);
  app.put(resultsURL, middleware.projectVersion.requireProjectVersionExist);
  app.put(resultsURL, middleware.user.getLockByVersion());
  app.put(resultsURL, middleware.project.requireProjectAccess);
  app.put(resultsURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.put(resultsURL, middleware.project.requireProjectAvailable);
  app.put(resultsURL, middleware.file.parseFileListQuery);
  app.put(resultsURL, middleware.function.parseFunctionListQuery);
  app.put(resultsURL, middleware.file.getFiles);
  app.put(resultsURL, middleware.function.getFunctions);
  app.put(resultsURL, middleware.file.filterFilesAvailable);
  app.put(resultsURL, runTests);

  // 部分执行测试用例
  app.put(partialResultsURL, middleware.license.detectSystemTimeTampering);
  app.put(partialResultsURL, middleware.user.requireTokenUnexpired);
  app.put(partialResultsURL, middleware.user.requireAvailable);
  app.put(partialResultsURL, middleware.user.requireTester);
  app.put(partialResultsURL, middleware.projectVersion.requireProjectVersionExist);
  app.put(partialResultsURL, middleware.user.getLockByVersion());
  app.put(partialResultsURL, middleware.project.requireProjectAccess);
  app.put(partialResultsURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.put(partialResultsURL, middleware.project.requireProjectAvailable);
  app.put(partialResultsURL, middleware.testCase.parseTestcaseListQuery);
  app.put(partialResultsURL, middleware.testCase.getTests);
  // check if the function is testable
  app.put(partialResultsURL, runPartialTests);

  // 导出目标机测试脚本
  app.put(targetMachineTestsExportUrl, middleware.license.detectSystemTimeTampering);
  app.put(targetMachineTestsExportUrl, middleware.user.requireTokenUnexpired);
  app.put(targetMachineTestsExportUrl, middleware.user.requireAvailable);
  app.put(targetMachineTestsExportUrl, middleware.user.requireTester);
  app.put(targetMachineTestsExportUrl, middleware.projectVersion.requireProjectVersionExist);
  app.put(targetMachineTestsExportUrl, middleware.project.requireProjectAccess);
  app.put(targetMachineTestsExportUrl, middleware.projectVersion.requireProjectVersionAvailable);
  app.put(targetMachineTestsExportUrl, middleware.project.requireProjectAvailable);
  app.put(targetMachineTestsExportUrl, middleware.file.parseFileListQuery);
  app.put(targetMachineTestsExportUrl, middleware.function.parseFunctionListQuery);
  app.put(targetMachineTestsExportUrl, middleware.file.getFiles);
  app.put(targetMachineTestsExportUrl, middleware.function.getFunctions);
  app.put(targetMachineTestsExportUrl, middleware.file.filterFilesAvailable);
  app.put(targetMachineTestsExportUrl, exportTargetMachineTests);

  app.get(targetMachineTestsDownloadUrl, middleware.user.requireTokenUnexpired);
  app.get(targetMachineTestsDownloadUrl, middleware.user.requireAvailable);
  app.get(targetMachineTestsDownloadUrl, middleware.user.requireTester);
  app.get(targetMachineTestsDownloadUrl, downloadTargetMachineTests);

  // 一键测试
  app.post(resultsURL, middleware.license.detectSystemTimeTampering);
  app.post(resultsURL, middleware.user.requireTokenUnexpired);
  app.post(resultsURL, middleware.user.requireAvailable);
  app.post(resultsURL, middleware.user.requireTester);
  app.post(resultsURL, middleware.projectVersion.requireProjectVersionExist);
  app.post(resultsURL, middleware.project.requireProjectAccess);
  app.post(resultsURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.post(resultsURL, middleware.function.validation);
  app.post(resultsURL, middleware.project.requireProjectAvailable);
  app.post(resultsURL, middleware.file.parseFileListQuery);
  app.post(resultsURL, middleware.function.parseFunctionListQuery);
  app.post(resultsURL, middleware.file.getFiles);
  app.post(resultsURL, middleware.function.getFunctions);
  app.post(resultsURL, middleware.file.filterFilesAvailable);
  app.post(resultsURL, autoTest);

  app.post(regressionResultsURL, middleware.license.detectSystemTimeTampering);
  app.post(regressionResultsURL, middleware.user.requireTokenUnexpired);
  app.post(regressionResultsURL, middleware.user.requireAvailable);
  app.post(regressionResultsURL, middleware.user.requireTester);
  app.post(regressionResultsURL, middleware.projectVersion.requireProjectVersionExist);
  app.post(regressionResultsURL, middleware.user.getLockByVersion());
  app.post(regressionResultsURL, middleware.project.requireProjectAccess);
  app.post(regressionResultsURL, middleware.projectVersion.requireProjectVersionAvailable);
  app.post(regressionResultsURL, middleware.function.validation);
  app.post(regressionResultsURL, middleware.project.requireProjectAvailable);
  app.post(regressionResultsURL, middleware.projectVersion.requireRegressionVersionExist);
  app.post(regressionResultsURL, middleware.file.parseFileListQuery);
  app.post(regressionResultsURL, middleware.function.parseFunctionListQuery);
  app.post(regressionResultsURL, middleware.file.getFiles);
  app.post(regressionResultsURL, middleware.function.getFunctions);
  app.post(regressionResultsURL, middleware.file.filterFilesAvailable);
  app.post(regressionResultsURL, regressionTest);

  // 项目总览-用例列表
  app.get(allTestsOverviewUrl, middleware.user.requireTokenUnexpired);
  app.get(allTestsOverviewUrl, middleware.user.requireAvailable);
  app.get(allTestsOverviewUrl, middleware.user.requireTester);
  app.get(allTestsOverviewUrl, middleware.projectVersion.requireProjectVersionExist);
  app.get(allTestsOverviewUrl, middleware.project.requireProjectAccess);
  app.get(allTestsOverviewUrl, allTestsOverview);

  // 项目总览-用力列表下载
  app.get(downloadTestsOverviewUrl, middleware.user.requireTokenUnexpired);
  app.get(downloadTestsOverviewUrl, middleware.user.requireAvailable);
  app.get(downloadTestsOverviewUrl, middleware.user.requireTester);
  app.get(downloadTestsOverviewUrl, middleware.projectVersion.requireProjectVersionExist);
  app.get(downloadTestsOverviewUrl, middleware.project.requireProjectAccess);
  app.get(downloadTestsOverviewUrl, exportTestsOverview);

  // 获取测试用例数据显示配置
  app.get(testsFieldConfigURL, middleware.user.requireTokenUnexpired);
  app.get(testsFieldConfigURL, middleware.user.requireAvailable);
  app.get(testsFieldConfigURL, middleware.user.requireTester);
  app.get(testsFieldConfigURL, middleware.function.requireFunctionExist);
  app.get(testsFieldConfigURL, middleware.project.requireProjectAccess);
  app.get(testsFieldConfigURL, getTestFieldConfig);
  // 测试用例数据显示配置更新
  app.put(testsFieldConfigURL, middleware.user.requireTokenUnexpired);
  app.put(testsFieldConfigURL, middleware.user.requireAvailable);
  app.put(testsFieldConfigURL, middleware.user.requireTester);
  app.put(testsFieldConfigURL, middleware.function.requireFunctionExist);
  app.put(testsFieldConfigURL, middleware.user.getLockByVersion());
  app.put(testsFieldConfigURL, middleware.project.requireProjectAccess);
  app.put(testsFieldConfigURL, updateTestFieldConfig);

  // 添加测试用例编组
  app.post(testCaseGroupURL, middleware.user.requireTokenUnexpired);
  app.post(testCaseGroupURL, middleware.user.requireAvailable);
  app.post(testCaseGroupURL, middleware.user.requireTester);
  app.post(testCaseGroupURL, middleware.function.requireFunctionExist);
  app.post(testCaseGroupURL, middleware.user.getLockByVersion());
  app.post(testCaseGroupURL, middleware.project.requireProjectAccess);
  app.post(testCaseGroupURL, addTestCaseGroup);

  // 获取测试用例编组
  app.get(testCaseGroupURL, middleware.user.requireTokenUnexpired);
  app.get(testCaseGroupURL, middleware.user.requireAvailable);
  app.get(testCaseGroupURL, middleware.user.requireTester);
  app.get(testCaseGroupURL, middleware.function.requireFunctionExist);
  app.get(testCaseGroupURL, middleware.project.requireProjectAccess);
  app.get(testCaseGroupURL, getTestCaseGroup);

  // 编辑测试用例编组
  app.put(editTestCaseGroupURL, middleware.user.requireTokenUnexpired);
  app.put(editTestCaseGroupURL, middleware.user.requireAvailable);
  app.put(editTestCaseGroupURL, middleware.user.requireTester);
  app.put(editTestCaseGroupURL, middleware.testCaseGroups.requireTestCaseGroupExist);
  app.put(editTestCaseGroupURL, middleware.user.getLockByVersion());
  app.put(editTestCaseGroupURL, middleware.project.requireProjectAccess);
  app.put(editTestCaseGroupURL, editTestCaseGroup);

  // 设置桩函数参数检查项
  app.post(stubParamsOutputURL, middleware.user.requireTokenUnexpired);
  app.post(stubParamsOutputURL, middleware.user.requireAvailable);
  app.post(stubParamsOutputURL, middleware.user.requireTester);
  app.post(stubParamsOutputURL, middleware.testCase.requireTestCaseExist);
  app.post(stubParamsOutputURL, middleware.project.requireProjectAccess);
  app.post(stubParamsOutputURL, addStubParamsOutput);

  // 取消桩函数参数检查项
  app.delete(stubParamsOutputURL, middleware.user.requireTokenUnexpired);
  app.delete(stubParamsOutputURL, middleware.user.requireAvailable);
  app.delete(stubParamsOutputURL, middleware.user.requireTester);
  app.delete(stubParamsOutputURL, middleware.testCase.requireTestCaseExist);
  app.delete(stubParamsOutputURL, middleware.project.requireProjectAccess);
  app.delete(stubParamsOutputURL, removeStubParamsOutput);

  // 添加桩函数参数
  app.post(stubVariadicParamsURL, middleware.user.requireTokenUnexpired);
  app.post(stubVariadicParamsURL, middleware.user.requireAvailable);
  app.post(stubVariadicParamsURL, middleware.user.requireTester);
  app.post(stubVariadicParamsURL, middleware.testCase.requireTestCaseExist);
  app.post(stubVariadicParamsURL, middleware.project.requireProjectAccess);
  app.post(stubVariadicParamsURL, addStubVariadicParams);

  // 设置数组显示下标
  app.post(arrayIndexRangeURL, middleware.user.requireTokenUnexpired);
  app.post(arrayIndexRangeURL, middleware.user.requireAvailable);
  app.post(arrayIndexRangeURL, middleware.user.requireTester);
  app.post(arrayIndexRangeURL, middleware.testCase.requireTestCaseExist);
  app.post(arrayIndexRangeURL, middleware.project.requireProjectAccess);
  app.post(arrayIndexRangeURL, updateArrayIndexRange);

  // 创建断点
  app.post(breakPointsURL, middleware.user.requireTokenUnexpired);
  app.post(breakPointsURL, middleware.user.requireAvailable);
  app.post(breakPointsURL, middleware.user.requireTester);
  app.post(breakPointsURL, middleware.testCase.requireTestCaseExist);
  app.post(breakPointsURL, middleware.project.requireProjectAccess);
  app.post(breakPointsURL, addBreakPoint);

  // 编辑断点
  app.put(breakPointURL, middleware.user.requireTokenUnexpired);
  app.put(breakPointURL, middleware.user.requireAvailable);
  app.put(breakPointURL, middleware.user.requireTester);
  app.put(breakPointURL, middleware.testCase.requireTestCaseExist);
  app.put(breakPointURL, middleware.project.requireProjectAccess);
  app.put(breakPointURL, middleware.testCase.requireBreakPointExist);
  app.put(breakPointURL, editBreakPoint);

  // 获取可观测的变量列表
  app.get(observableVariableURL, middleware.user.requireTokenUnexpired);
  app.get(observableVariableURL, middleware.user.requireAvailable);
  app.get(observableVariableURL, middleware.user.requireTester);
  app.get(observableVariableURL, middleware.testCase.requireTestCaseExist);
  app.get(observableVariableURL, middleware.project.requireProjectAccess);
  app.get(observableVariableURL, getObservableVariables);

  // 获取断点
  app.get(breakPointURL, middleware.user.requireTokenUnexpired);
  app.get(breakPointURL, middleware.user.requireAvailable);
  app.get(breakPointURL, middleware.user.requireTester);
  app.get(breakPointURL, middleware.testCase.requireTestCaseExist);
  app.get(breakPointURL, middleware.project.requireProjectAccess);
  app.get(breakPointURL, middleware.testCase.requireBreakPointExist);
  app.get(breakPointURL, getBreakPoint);

  // 删除断点
  app.delete(breakPointURL, middleware.user.requireTokenUnexpired);
  app.delete(breakPointURL, middleware.user.requireAvailable);
  app.delete(breakPointURL, middleware.user.requireTester);
  app.delete(breakPointURL, middleware.testCase.requireTestCaseExist);
  app.delete(breakPointURL, middleware.project.requireProjectAccess);
  app.delete(breakPointURL, middleware.testCase.requireBreakPointExist);
  app.delete(breakPointURL, deleteBreakPoint);
  // 设置断点数组变量下标
  app.put(breakPointArrayIndexRangeURL, middleware.user.requireTokenUnexpired);
  app.put(breakPointArrayIndexRangeURL, middleware.user.requireAvailable);
  app.put(breakPointArrayIndexRangeURL, middleware.user.requireTester);
  app.put(breakPointArrayIndexRangeURL, middleware.testCase.requireBreakPointExist);
  app.put(breakPointArrayIndexRangeURL, updateBreakPointArrayIndexRange);
  // 设置断点变量值
  app.put(breakPointVariableURL, middleware.user.requireTokenUnexpired);
  app.put(breakPointVariableURL, middleware.user.requireAvailable);
  app.put(breakPointVariableURL, middleware.user.requireTester);
  app.put(breakPointVariableURL, middleware.testCase.requireBreakPointExist);
  app.put(breakPointVariableURL, updateBreakPointVaraibleValue);
  // 添加指针变量的指针目标
  app.post(breakPointPointTargetsURL, middleware.user.requireTokenUnexpired);
  app.post(breakPointPointTargetsURL, middleware.user.requireAvailable);
  app.post(breakPointPointTargetsURL, middleware.user.requireTester);
  app.post(breakPointPointTargetsURL, middleware.testCase.requireBreakPointExist);
  app.post(breakPointPointTargetsURL, addBreakPointPointTargets);
  // 测试用例断点列表
  app.get(testCaseBreakPointListURL, middleware.user.requireTokenUnexpired);
  app.get(testCaseBreakPointListURL, middleware.user.requireAvailable);
  app.get(testCaseBreakPointListURL, middleware.user.requireTester);
  app.get(testCaseBreakPointListURL, middleware.testCase.requireTestCaseExist);
  app.get(testCaseBreakPointListURL, testCaseBreakPointList);

  // 批量删除测试用例
  app.put(bulkDeleteTestcaseURL, middleware.user.requireTokenUnexpired);
  app.put(bulkDeleteTestcaseURL, middleware.user.requireAvailable);
  app.put(bulkDeleteTestcaseURL, middleware.user.requireTester);
  app.put(bulkDeleteTestcaseURL, bulkDeleteTestcases);
  // 重置测试用例number
  app.put(testsNumberURL, middleware.user.requireTokenUnexpired);
  app.put(testsNumberURL, middleware.user.requireAvailable);
  app.put(testsNumberURL, middleware.user.requireTester);
  app.put(testsNumberURL, middleware.function.requireFunctionExist);
  app.put(testsNumberURL, middleware.project.requireProjectAccess);
  app.put(testsNumberURL, resetTestNumber);
  // 获取测试用例，variable以及用到的类型系统
  app.get(testcaseCombinedURL, middleware.user.requireTokenUnexpired);
  app.get(testcaseCombinedURL, middleware.user.requireAvailable);
  app.get(testcaseCombinedURL, middleware.user.requireTester);
  app.get(testcaseCombinedURL, getTestcaseCombined);
};
